package cn.changeforyou.analyzer.javaparse;

import cn.changeforyou.analyzer.ParseException;
import cn.changeforyou.analyzer.javaparse.model.EntityModel;
import cn.changeforyou.analyzer.javaparse.model.EntityModel.FieldInfo;
import cn.changeforyou.analyzer.javaparse.visitors.PairsAnnotationVisitor;
import cn.changeforyou.analyzer.javaparse.visitors.ReturnTypeParam;
import cn.changeforyou.analyzer.javaparse.visitors.SingleAnnotationStringValueVisitor;
import cn.changeforyou.utils.io.read.ReadUtils;
import com.baomidou.mybatisplus.annotation.TableField;
import com.github.javaparser.JavaParser;
import com.github.javaparser.ParserConfiguration;
import com.github.javaparser.ParserConfiguration.LanguageLevel;
import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.expr.AnnotationExpr;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.javadoc.Javadoc;
import com.github.javaparser.javadoc.JavadocBlockTag;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * @author zhyu
 * @date 2021/2/4 10:04
 */
public class JavaParseUtils {
	
	/**
	 * 根据类名和项目地址解析mybatis-plus的实体类
	 *
	 * @param entityName
	 * @param projectPath
	 * @return
	 */
	public static EntityModel getJavaEntityModelFromEntityNameAndProjectPath(String entityName, String projectPath) {
		File file = ProjectUtils.findFileInMavenProject(entityName + ".java", projectPath);
		if (null == file) {
			return null;
		}
		EntityModel entityModel = new EntityModel();
		ClassOrInterfaceDeclaration classOrInterfaceDeclaration = getEntityModel(entityName, file, entityModel);
		NodeList<ClassOrInterfaceType> extendedTypes = classOrInterfaceDeclaration.getExtendedTypes();
		if (!extendedTypes.isEmpty()) {
			ClassOrInterfaceType superType = extendedTypes.get(0);
			String superClassName = superType.getName().getIdentifier();
			File superClassFile = ProjectUtils.findFileInMavenProject(superClassName + ".java", projectPath);
			if (null == superClassFile) {
				throw new ParseException(entityName + "有父类[" + superClassName + "]， 但是父类没有找到， 预计不在本项目的java文件中");
			}
			getSuperClassEntityModel(superClassName, superClassFile, entityModel);
		}
		entityModel.setClassOrInterfaceDeclaration(classOrInterfaceDeclaration);
		entityModel.setFile(file);
		return entityModel;
	}
	
	/**
	 * 根据类名和项目地址解析mybatis-plus的实体类
	 *
	 * @param entityName
	 * @param projectPath
	 * @return
	 */
	public static EntityModel getMybatisPlusEntityModelFromEntityNameAndProjectPath(String entityName, String projectPath) {
		File file = ProjectUtils.findFileInMavenProject(entityName + ".java", projectPath);
		if (null == file) {
			return null;
		}
		EntityModel entityModel = new EntityModel();
		ClassOrInterfaceDeclaration classOrInterfaceDeclaration = getMybatisPlusEntityModel(entityName, file, entityModel);
		NodeList<ClassOrInterfaceType> extendedTypes = classOrInterfaceDeclaration.getExtendedTypes();
		if (!extendedTypes.isEmpty()) {
			ClassOrInterfaceType superType = extendedTypes.get(0);
			String superClassName = superType.getName().getIdentifier();
			File superClassFile = ProjectUtils.findFileInMavenProject(superClassName + ".java", projectPath);
			if (null == superClassFile) {
				throw new ParseException(entityName + "有父类[" + superClassName + "]， 但是父类没有找到， 预计不在本项目的java文件中");
			}
			getSuperClassEntityModel(superClassName, superClassFile, entityModel);
		}
		entityModel.setClassOrInterfaceDeclaration(classOrInterfaceDeclaration);
		entityModel.setFile(file);
		return entityModel;
	}
	
	/**
	 * 解析java实体类, 注释支持各种类型
	 *
	 * @param entityName
	 * @param file
	 * @param entityModel
	 * @return
	 */
	private static ClassOrInterfaceDeclaration getMybatisPlusEntityModel(String entityName, File file, EntityModel entityModel) {
		ClassOrInterfaceDeclaration classOrInterfaceDeclaration = JavaParseUtils.parseFileAsClass(file, entityName);
		AnnotationExpr tableAnnotation = classOrInterfaceDeclaration.getAnnotationByName("TableName").get();
		AnnotationExpr apiModel = classOrInterfaceDeclaration.getAnnotationByName("ApiModel").get();
		
		SingleAnnotationStringValueVisitor singleAnnotationStringValueVisitor = new SingleAnnotationStringValueVisitor();
		String tableName = tableAnnotation.accept(singleAnnotationStringValueVisitor, null);
		String entityComment = apiModel.accept(singleAnnotationStringValueVisitor, "description");
		List<FieldDeclaration> fields = classOrInterfaceDeclaration.getFields();
		
		entityModel.setTableComment(entityComment);
		List<FieldInfo> fieldInfos = new ArrayList<>();
		entityModel.setFieldInfos(fieldInfos);
		entityModel.setTableName(tableName);
		for (FieldDeclaration field : fields) {
			if (field.isPrivate() && !field.isStatic()) {
				VariableDeclarator variable = field.getVariable(0);
				String filedName = variable.getName().getIdentifier();
				String javaType = variable.getType().asClassOrInterfaceType().getName().getIdentifier();
				AnnotationExpr apiModelProperty = field.getAnnotationByName("ApiModelProperty").get();
				String comment = apiModelProperty.accept(singleAnnotationStringValueVisitor, null);
				
				//过滤掉 @TableField(exist = false)的情况
				Optional<AnnotationExpr> tableFieldOptional = field.getAnnotationByClass(TableField.class);
				if (tableFieldOptional.isPresent()) {
					AnnotationExpr expr = tableFieldOptional.get();
					PairsAnnotationVisitor<Boolean> pairsAnnotationVisitor = new PairsAnnotationVisitor();
					Boolean exist = expr.accept(pairsAnnotationVisitor, new ReturnTypeParam<>(Boolean.class, "exist"));
					if (null != exist && !exist) {
						continue;
					}
				}
				
				FieldInfo fieldInfo = new FieldInfo(entityModel);
				fieldInfo.setComment(comment);
				fieldInfo.setJavaType(javaType);
				fieldInfo.setName(filedName);
				fieldInfos.add(fieldInfo);
			}
		}
		return classOrInterfaceDeclaration;
	}
	
	/**
	 * 解析标准的mybatisPlus 实体类信息, 注释必须在swagger2注解里
	 *
	 * @param entityName
	 * @param file
	 * @param entityModel
	 * @return
	 */
	private static ClassOrInterfaceDeclaration getEntityModel(String entityName, File file, EntityModel entityModel) {
		ClassOrInterfaceDeclaration classOrInterfaceDeclaration = JavaParseUtils.parseFileAsClass(file, entityName);
		Javadoc javadoc = classOrInterfaceDeclaration.getJavadocComment().get().parse();
		List<JavadocBlockTag> javaDocTags = javadoc.getBlockTags();
		boolean hasTableNames = false;
		if (!javaDocTags.isEmpty()) {
			for (JavadocBlockTag javaDocTag : javaDocTags) {
				if (javaDocTag.getTagName().equals("table")) {
					entityModel.setTableName(javaDocTag.getContent().toText());
					hasTableNames = true;
					entityModel.setTableComment(javadoc.getDescription().toText());
					break;
				}
			}
		}
		if (!hasTableNames) {
			throw new ParseException("java文件里面没哟表名信息, 请在class上添加javaDoc标签, @table, 在标签上面添加表的注释");
		}
		List<FieldDeclaration> fields = classOrInterfaceDeclaration.getFields();
		SingleAnnotationStringValueVisitor singleAnnotationStringValueVisitor = new SingleAnnotationStringValueVisitor();
		
		List<FieldInfo> fieldInfos = new ArrayList<>();
		entityModel.setFieldInfos(fieldInfos);
		boolean hasId = false;
		for (FieldDeclaration field : fields) {
			if (!field.isPublic() && !field.isStatic()) {
				VariableDeclarator variable = field.getVariable(0);
				String filedName = variable.getName().getIdentifier();
				String javaType = variable.getType().asClassOrInterfaceType().getName().getIdentifier();
				Optional<AnnotationExpr> tableFieldOptional = field.getAnnotationByClass(TableField.class);
				String comment;
                FieldInfo fieldInfo = new FieldInfo(entityModel);
				if (tableFieldOptional.isPresent()) {
					AnnotationExpr apiModelProperty = field.getAnnotationByName("ApiModelProperty").get();
					comment = apiModelProperty.accept(singleAnnotationStringValueVisitor, null);
					//过滤掉 @TableField(exist = false)的情况
					
					PairsAnnotationVisitor<Boolean> pairsAnnotationVisitor = new PairsAnnotationVisitor();
					Boolean exist = tableFieldOptional.get().accept(pairsAnnotationVisitor, new ReturnTypeParam<>(Boolean.class, "exist"));
					if (null != exist && !exist) {
						continue;
					}
				}else {
                    Javadoc filedJavaDoc = field.getJavadocComment().get().parse();
                    List<JavadocBlockTag> blockTags = filedJavaDoc.getBlockTags();
                    if (!blockTags.isEmpty()) {
                        for (JavadocBlockTag blockTag : blockTags) {
                            if(blockTag.getTagName().equals("id")) {
                                hasId = true;
                                fieldInfo.setPrimaryKey(true);
                            }
                        }
                    }
                    comment = filedJavaDoc.getDescription().toText();
                }
				
				fieldInfo.setComment(comment);
				fieldInfo.setJavaType(javaType);
				fieldInfo.setName(filedName);
				fieldInfos.add(fieldInfo);
			}
			
		}
        if(!hasId) {
            throw new ParseException("字段上没有标注id, 请添加javadoc的标签@id");
        }
		return classOrInterfaceDeclaration;
	}
	
	private static void getSuperClassEntityModel(String entityName, File file, EntityModel entityModel) {
		ClassOrInterfaceDeclaration classOrInterfaceDeclaration = JavaParseUtils.parseFileAsClass(file, entityName);
		
		SingleAnnotationStringValueVisitor singleAnnotationStringValueVisitor = new SingleAnnotationStringValueVisitor();
		List<FieldDeclaration> fields = classOrInterfaceDeclaration.getFields();
		
		List<FieldInfo> fieldInfos = entityModel.getFieldInfos();
		entityModel.setFieldInfos(fieldInfos);
		for (FieldDeclaration field : fields) {
			if (field.isPrivate() && !field.isStatic()) {
				VariableDeclarator variable = field.getVariable(0);
				String filedName = variable.getName().getIdentifier();
				String javaType = variable.getType().asClassOrInterfaceType().getName().getIdentifier();
				
				FieldInfo fieldInfo = new FieldInfo(entityModel);
				fieldInfo.setJavaType(javaType);
				fieldInfo.setName(filedName);
				fieldInfos.add(fieldInfo);
			}
		}
	}
	
	/**
	 * 转换java文件为编译单元
	 *
	 * @param file
	 * @return
	 */
	public static CompilationUnit parseFileAsCompilationUnit(File file) {
		ParserConfiguration parserConfiguration = new ParserConfiguration();
		parserConfiguration.setLanguageLevel(LanguageLevel.JAVA_8);
		JavaParser javaParser = new JavaParser(parserConfiguration);
		try {
			String code = ReadUtils.readReaderToString(new FileReader(file));
//            String l1 = "@Data";
//            String l2 = "import lombok.Data;";
//            int index1 = code.indexOf(l1);
//            if(index1 != -1) {
//                code = code.replace(l1, "");
//                code = code.replace(l2, "");
//            }
//            System.out.println(code);
			return StaticJavaParser.parse(file);
//            return javaParser.parse(code).getResult().get();
		} catch (FileNotFoundException e) {
			throw new RuntimeException(e);
		}
	}
	
	/***
	 * 转换java文件和类为类装饰器
	 * @param file
	 * @param className
	 * @return
	 */
	public static ClassOrInterfaceDeclaration parseFileAsClass(File file, String className) {
		CompilationUnit compilationUnit = parseFileAsCompilationUnit(file);
		if (null != compilationUnit) {
			Optional<ClassOrInterfaceDeclaration> optional = compilationUnit.getClassByName(className);
			if (optional.isPresent()) {
				return optional.get();
			} else {
				throw new RuntimeException("文件[" + file.getName() + "]中不包含class[" + className + "]");
			}
		}
		return null;
	}
}
